﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.Threading;
using System.Windows.Forms.DataVisualization.Charting;
using nGSO.Performance;
using nGSO.Model;

namespace nGSO.UI
{
    public partial class GsoTab : UserControl
    {
        #region
        
        private SelectFitnessFunction _showFitnessFunSelector;
        private GsoConfig _config;
        private IGsoEngine _engine;
        private Thread _runGSOAlgo;
        private SearchSpace _result;
        private string _seriesName = "position";
        private string _seriesOptimums = "optimums";
        private PerfManager _perfTracker;

        #endregion

        public ToolStripButton SaveToolStrip { get; set; }

        public GsoTab()
        {
            InitializeComponent();

            _config = new GsoConfig();
            bindingGsoConfig.DataSource = _config;
            bindingOptimizationTaskElement.DataSource = _config.SelectedOptimizationTask;

            _showFitnessFunSelector = new SelectFitnessFunction();
            _showFitnessFunSelector.SetAgentFunctions(_config);

            hlkSelectedAgent.Text = _config.SelectedOptimizationTask.Name;

            foreach (var extension in _config.AfterAgentMoveExtensions)
            {
                cblExtensions.Items.Add(extension);
            }

            _perfTracker = new PerfManager();
        }

        private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            DialogResult result = _showFitnessFunSelector.ShowDialog();
            switch (result)
            {
                case DialogResult.Abort:
                    break;
                case DialogResult.Cancel:
                    break;
                case DialogResult.Ignore:
                    break;
                case DialogResult.No:
                    break;
                case DialogResult.None:
                    break;
                case DialogResult.OK:
                    _config.SetOptimizationTaskParameters();
                    bindingGsoConfig.ResetBindings(false);
                    bindingOptimizationTaskElement.ResetBindings(false);
                    _config.CompileFitnessEvaluator();
                    hlkSelectedAgent.Text = _config.SelectedOptimizationTask.Name;
                    break;
                case DialogResult.Retry:
                    break;
                case DialogResult.Yes:
                    break;
                default:
                    break;
            }
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            btnStart.Enabled = false;
            btnStop.Enabled = true;
            btnPause.Enabled = true;
            
            progressBar.Maximum = _config.MaxIterations;
            progressBar.Minimum = 0;
            _engine = new GsoEngine(_config);
            _engine.IterationCompleted += new IterationCompletedHandler(engine_IterationCompleted);
            _config.StepInterval = 3;

            SetChart();

            AddExpectedOptimums();

            DateTime startTime = DateTime.Now;

            ThreadStart start = new ThreadStart(delegate()
                {
                    RunGSO();
                });
            
            _runGSOAlgo = new Thread(start) { Name = "GsoEngine", IsBackground = true };
            _runGSOAlgo.Start();
            
        }

        private void RunGSO()
        {
            var perfEntry = _perfTracker.StartTest(_config);

            _result = _engine.StartGSOSolving();

            if (_result == null && _engine.CurrentState == GsoEngineStates.Stoped)
            {
                btnPause.Enabled = btnStop.Enabled = false;
                MessageBox.Show("Gso engine stoped!");
                _perfTracker.Delete(perfEntry);
            }

            if (_result != null)
            {
                this.Invoke(new Action(delegate()
                {
                    btnStop.Enabled = btnPause.Enabled = false;
                }));

                _perfTracker.AddResult(perfEntry, _result, _config);

                MessageBox.Show("Gso engine returned result! ");
            }
            
            
            splitContainer1.Invoke(new Action(delegate()
            {
                progressBar.Value = 0;
            }));

            this.Invoke(new Action(delegate()
            {
                btnStart.Enabled = true;
            }));

        }

        private void AddExpectedOptimums()
        {
            string encodedOptima = _config.SelectedOptimizationTask.EncodedOptima;

            Utils.OptimumCsvProvider optimumProvider = new Utils.OptimumCsvProvider();
            List<double[]> optimums = optimumProvider.CreateFromConfigValue(encodedOptima);

            Series serie = chart1.Series.Add(_seriesOptimums);
            serie.MarkerStyle = MarkerStyle.Circle;
            serie.MarkerSize = 3;
            serie.Color = Color.Red;
            serie.BorderColor = Color.DarkRed;
            serie.BorderWidth = 2;
            serie.BorderDashStyle = ChartDashStyle.Solid;
            serie.IsValueShownAsLabel = false;
            serie.XValueType = ChartValueType.Int32;
            serie.YValueType = ChartValueType.Int32;

            var locations = optimums.Select(s => new { x = s[0], y = s[1] });
            serie.ChartType = SeriesChartType.FastPoint;
            serie.Points.DataBind(locations, "x", "y", null);
            
        }

        private void SetChart()
        {
            chart1.Series.Clear();
            chart1.Series.Add(_seriesName);
            chart1.Series[_seriesName].BorderColor = Color.Black;
            chart1.Series[_seriesName].MarkerStyle = MarkerStyle.Circle;
            chart1.Series[_seriesName].MarkerColor = Color.Black;
            chart1.Series[_seriesName].MarkerSize = 4;

            chart1.Series[_seriesName].IsValueShownAsLabel = false;
            chart1.Series[_seriesName].XValueType = ChartValueType.Int32;
            chart1.Series[_seriesName].YValueType = ChartValueType.Int32;

            chart1.ChartAreas.First().AxisX.Interval = 1;
            chart1.ChartAreas.First().AxisY.Interval = 1;

        }


        void engine_IterationCompleted(object sender, IterationProgressEventArgs e)
        {
            if (e.CurrentStep % _config.StepInterval == 0)
            {
                    chart1.Invoke(new Action(delegate()
                    {
                        var locations = e.LastIterationHistory.Select(s => new { x = s.Loc[0], y = s.Loc[1] });
                        chart1.Series["position"].ChartType = SeriesChartType.FastPoint;
                        chart1.Series["position"].Points.DataBind(locations, "x", "y", null);
                        
                    }));
            }

            splitContainer1.Invoke(new Action(delegate()
                {
                    progressBar.Value = (int)e.CurrentStep;
                }));
        }

        private void btnPause_Click(object sender, EventArgs e)
        {
            lock (_engine)
            {
                if (_engine.CurrentState == GsoEngineStates.Running)
                {
                    _engine.Pause();
                    btnPause.Text = "RESUME";
                }
                else if (_engine.CurrentState == GsoEngineStates.Paused)
                { 
                    _engine.Resume();
                    btnPause.Text = "PAUSE";
                }
            }
        }

        private void btnReset_Click(object sender, EventArgs e)
        {
            _engine.Stop();
            btnStop.Enabled = false;
            btnPause.Enabled = false;
            btnStart.Enabled = true;
        }

        private void cblExtensions_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            var extension = (BaseAfterAgentMoveExtension)cblExtensions.SelectedItem;
            extension.Enabled = (e.NewValue == CheckState.Checked);
        }

        internal SearchSpace GetResult()
        {
            return _result; 
        }

        private void chart1_MouseMove(object sender, MouseEventArgs e)
        {
            HitTestResult result = chart1.HitTest(e.X, e.Y);

            if (result.ChartElementType == ChartElementType.DataPoint)
            {
                lblPosition.Text = result.Object.ToString();
            }
        }
    }
}
